package com.youlu.campus.service.commission;

import com.alibaba.fastjson.JSON;
//import com.ctrip.framework.apollo.Config;
//import com.ctrip.framework.apollo.ConfigService;
import com.google.common.collect.Lists;
import com.mongodb.client.result.UpdateResult;
import com.youlu.campus.base.exception.BusinessException;
import com.youlu.campus.base.user.UserWithdraw;
import com.youlu.campus.common.utils.IdGeneratorUtils;
import com.youlu.campus.entity.SystemApolloConfig;
import com.youlu.campus.entity.WechatMerchantConfig;
import com.youlu.campus.entity.commission.*;
import com.youlu.campus.entity.commission.dto.*;
import com.youlu.campus.entity.commission.vo.DistributorAccountVO;
import com.youlu.campus.entity.commission.vo.DistributorAccountWithdrawVO;
import com.youlu.campus.entity.course.dto.GoodsOrderDTO;
import com.youlu.campus.service.course.WechatPayService;
import com.youlu.campus.service.system.SystemApolloConfigService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Triple;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.repository.support.PageableExecutionUtils;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @author : [dengbo]
 * @className : DistributorAccountService
 * @description : [分销者账户信息]
 * @createTime : [2023/2/6 11:47]
 */
@Service
@Slf4j
public class DistributorAccountService {
    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private WechatPayService wechatPayService;
    @Autowired
    private DistributorApplyService distributorApplyService;
    @Autowired
    private DistributorGradeService distributorGradeService;
    @Autowired
    private SystemApolloConfigService systemApolloConfigService;

    /**
     * 查询提现列表
     * @param query
     * @return
     */
    public Page<DistributorAccountWithdrawVO> getWithdrawList(DistributorAccountWithdrawQuery query) {
        Query q = new Query();
        q.addCriteria(Criteria.where("accountId").is(query.getAccountId()));
        long count = mongoTemplate.count(q, DistributorAccountWithdraw.class);
        q.with(Sort.by(Sort.Order.desc("createdTime")));
        Pageable pageRequest = PageRequest.of(query.getPage() - 1, query.getPageSize());
        q.with(pageRequest);
        List<DistributorAccountWithdraw> list = mongoTemplate.find(q, DistributorAccountWithdraw.class);
        List<DistributorAccountWithdrawVO> re = list.stream().map(item -> {
            DistributorAccountWithdrawVO vo = new DistributorAccountWithdrawVO();
            BeanUtils.copyProperties(item, vo);
            return vo;
        }).collect(Collectors.toList());
        return PageableExecutionUtils.getPage(re, pageRequest, () -> count);
    }

    /**
     * 查询后台提现列表
     * @param query
     * @return
     */
    public Page<DistributorAccountWithdraw> getAdminWithdrawList(DistributorAccountWithdrawSearchDTO query) {
        Query q = new Query();
        if (StringUtils.isNotBlank(query.getName())) {
            q.addCriteria(Criteria.where("name").is(query.getName()));
        }
        if (StringUtils.isNotBlank(query.getMobile())) {
            q.addCriteria(Criteria.where("mobile").is(query.getMobile()));
        }
        if (StringUtils.isNotBlank(query.getOpenId())) {
            q.addCriteria(Criteria.where("openId").is(query.getOpenId()));
        }
        long count = mongoTemplate.count(q, DistributorAccountWithdraw.class);
        q.with(Sort.by(Sort.Order.desc("createdTime")));
        Pageable pageRequest = PageRequest.of(query.getPage() - 1, query.getPageSize());
        q.with(pageRequest);
        List<DistributorAccountWithdraw> list = mongoTemplate.find(q, DistributorAccountWithdraw.class);
        return PageableExecutionUtils.getPage(list, pageRequest, () -> count);
    }

    /**
     * 后台提现操作
     * @param dto
     * @return
     */
    public boolean adminAdminWithdrawOperation(DistributorAccountWithdrawOperationDTO dto) {
        DistributorAccountWithdraw withdraw = mongoTemplate.findById(dto.getId(), DistributorAccountWithdraw.class);
        if (withdraw == null) {
            throw new BusinessException("提现记录不存在");
        }
        if (!Lists.newArrayList(0, 1).contains(dto.getStatus())) {
            throw new BusinessException("状态错误");
        }
        Query query = new Query(Criteria.where("id").is(dto.getId()));
        query.addCriteria(Criteria.where("status").is(2));
        // 是否通过
        boolean pass = dto.getStatus().equals(0);
        Update update = new Update();
        update.set("status", pass ? 0 : 1);
        update.set("operator", dto.getOperator());
        UpdateResult result = mongoTemplate.updateFirst(query, update, DistributorAccountWithdraw.class);
        if (!pass && result.getModifiedCount() > 0) {
            // 回滚账户余额
            rollBackAccountBalance(withdraw);
        }
        return true;
    }


    /**
     * 绑定支付宝账户
     * @param query
     * @return
     */
    public boolean bindAlipay(DistributorAccountBindAccountDTO query) {
        DistributorAccount account = queryAccountByUserId(query.getUserId());
        if (account == null) {
            throw new BusinessException("账户信息不存在");
        }
        boolean isAlipayAccount = Pattern.matches("^(?:1[3-9]\\d{9}|[a-zA-Z\\d._-]*\\@[a-zA-Z\\d.-]{1,10}\\.[a-zA-Z\\d]{1,20})$", query.getAlipayAccount());
        if (!isAlipayAccount) {
            throw new BusinessException("请输入手机号或邮箱");
        }
        Query q = new Query(Criteria.where("accountId").is(query.getUserId()));
        Update update = new Update();
        update.set("alipayAccount", query.getAlipayAccount());
        UpdateResult result = mongoTemplate.updateFirst(q, update, DistributorAccount.class);
        return result.getModifiedCount() > 0;
    }

    public DistributorAccountVO getAccountInfo(String userId) {
        DistributorAccountVO vo = new DistributorAccountVO();
        // 查询账户信息
        DistributorAccount account = queryAccountByUserId(userId);
        vo.setAccountBalance(0L);
        vo.setAccountId(userId);
        if (account != null) {
            vo.setAccountBalance(account.getAccountBalance());
            vo.setAlipayAccount(account.getAlipayAccount());
        }
        // 查询等级信息
        DistributorAccountGradeDTO dto = distributorGradeService.getDistributorRate(userId);
        if (StringUtils.isBlank(dto.getGradeName())) {
            vo.setGradeName(dto.getGradeName());
            vo.setGradeInfo("");
        } else {
            vo.setGradeName(dto.getGradeName());
            vo.setGradeInfo("当前等级" + dto.getGradeName() + "，提升至下一等级得更多佣金，点击查看");
        }
        // 查询配置信息
        DistributionContentConfig contentConfig = mongoTemplate.findOne(new Query(), DistributionContentConfig.class);
        if (contentConfig != null) {
            vo.setLevelDesc(contentConfig.getLevelDesc());
            vo.setRuleDesc(contentConfig.getRuleDesc());
        }
        return vo;
    }

    public DistributorApplyUserInfoDTO personalCheck(String userId, Integer type) {
        if (type == null || type == 0) {
            DistributorApply apply = distributorApplyService.findByUserId(userId, DistributorApplyStatusEnum.PASS.getValue());
            if (apply == null) {
                throw new BusinessException("账户不存在或者审核未通过");
            }
            DistributorApplyUserInfoDTO dto = new DistributorApplyUserInfoDTO();
            dto.setName(apply.getName());
            dto.setMobile(apply.getMobile());
            return dto;
        }
        // TODO 渠道
        return null;
    }

    /**
     * 提现
     * @param query
     * @return
     */
    public boolean distributorWithdraw(DistributorAccountWithdrawDTO query) {
        if (query.getMoney() <= 0) {
            throw new BusinessException("提现金额不能小于0");
        }
        String accountId = query.getUserId();
        Integer money = query.getMoney();
        DistributorApplyUserInfoDTO applyInfo = personalCheck(accountId, query.getType());
        DistributorAccount account = queryAccountByUserId(accountId);
        if (account == null || account.getAccountBalance() <= 0) {
            throw new BusinessException("暂无余额可以提现");
        }
        if (money > account.getAccountBalance()) {
            throw new BusinessException("余额不足");
        }
        // 添加开关
//        Config config = ConfigService.getAppConfig();
//        String cnf = config.getProperty("user.withdraw.gray", "{}");
//        UserWithdraw userWithdraw = JSON.parseObject(cnf, UserWithdraw.class);
        SystemApolloConfig systemApolloConfig = systemApolloConfigService.find();
        UserWithdraw userWithdraw=systemApolloConfig.getUserWithdraw();
        if (userWithdraw.getDisabled()) {
            throw new BusinessException(userWithdraw.getMessage());
        }
        if (userWithdraw.getOpened()) {
            if (!userWithdraw.getUserIdList().contains(accountId)) {
                throw new BusinessException(userWithdraw.getMessage());
            }
        }
        if (StringUtils.isBlank(account.getAlipayAccount())) {
            throw new BusinessException("请先绑定支付宝账号");
        }
        // 扣减金额
        subAccount(accountId, money);
        //支付宝打款 线下打款
        DistributorAccountWithdraw withdraw = buildAlipayOffline(applyInfo, query, account.getAlipayAccount());
        mongoTemplate.insert(withdraw);
        return true;
    }

    public void subAccount(String accountId, Integer money) {
        //微信打款
        Query q = new Query(Criteria.where("accountId").is(accountId));
        q.addCriteria(Criteria.where("accountBalance").gte(money));
        Update update = new Update();
        update.inc("accountBalance", -money);
        update.set("updatedTime", new Date());
        UpdateResult result = mongoTemplate.updateFirst(q, update, DistributorAccount.class);
        if (result.getModifiedCount() <= 0) {
            throw new BusinessException("余额不足");
        }
    }

    public DistributorAccountWithdraw buildAlipayOffline(DistributorApplyUserInfoDTO applyInfo, DistributorAccountWithdrawDTO query, String alipayAccount) {
        //支付宝打款 线下打款
        DistributorAccountWithdraw withdraw = new DistributorAccountWithdraw();
        withdraw.setName(applyInfo.getName());
        withdraw.setMobile(applyInfo.getMobile());
        withdraw.setMoney(query.getMoney());
        withdraw.setOpenId(query.getOpenId());
        withdraw.setAccountId(query.getUserId());
        withdraw.setCreatedTime(new Date());
        withdraw.setWithdrawType("alipay");
        String orderNo = IdGeneratorUtils.generateWithdraw();
        withdraw.setOrderNo(orderNo);
        withdraw.setAlipayAccount(alipayAccount);
        // 个人提现
        withdraw.setType(0);
        withdraw.setStatus(2);
        return withdraw;
    }

    public DistributorAccountWithdraw buildWechatPay(DistributorApplyUserInfoDTO applyInfo, DistributorAccountWithdrawDTO query) {
        DistributorAccountWithdraw withdraw = new DistributorAccountWithdraw();
        withdraw.setName(applyInfo.getName());
        withdraw.setMobile(applyInfo.getMobile());
        withdraw.setMoney(query.getMoney());
        withdraw.setOpenId(query.getOpenId());
        withdraw.setAppId(query.getOpenId());
        withdraw.setAccountId(query.getUserId());
        withdraw.setCreatedTime(new Date());
        withdraw.setWithdrawType("wechat-pay");
        String orderNo = IdGeneratorUtils.generateWithdraw();
        withdraw.setOrderNo(orderNo);
        withdraw.setAppId(query.getAppId());
        // 个人提现
        withdraw.setType(0);
        return withdraw;
    }

    public void wechatPay(DistributorAccountWithdraw withdraw) {
        // 获取商户信息
        WechatMerchantConfig merchantInfo = wechatPayService.getRandomMerchant(withdraw.getAppId());
        // 添加商户信息
        withdraw.setMerchantId(merchantInfo.getMerchantId());
        withdraw.setMerchantName(merchantInfo.getMerchantName());
        GoodsOrderDTO dto = new GoodsOrderDTO();
        dto.setOpenId(withdraw.getOpenId());
        dto.setPrice(withdraw.getMoney());
        dto.setAppId(withdraw.getAppId());
        dto.setOrderNo(withdraw.getOrderNo());
        // 获取转账结果
        Triple<Boolean, Map, String> re = wechatPayService.transfer(dto, merchantInfo);
        if (!re.getLeft()) {
            withdraw.setStatus(1);
            withdraw.setResult(re.getMiddle());
            mongoTemplate.insert(withdraw);
            // 回滚扣减金额
            rollBackAccountBalance(withdraw);
            throw new BusinessException(re.getRight());
        }
        withdraw.setStatus(0);
        mongoTemplate.insert(withdraw);
    }

    /**
     * 回滚账户信息
     * @param withdraw
     */
    public void rollBackAccountBalance(DistributorAccountWithdraw withdraw) {
        Integer money = withdraw.getMoney();
        String accountId = withdraw.getAccountId();
        boolean isNotChannel = withdraw.getType() == null || withdraw.getType() == 0;
        Query q = new Query();
        if (isNotChannel) {
            q.addCriteria(Criteria.where("accountId").is(accountId));
        } else {
            q.addCriteria(Criteria.where("id").is(accountId));
        }
        Update update = new Update();
        update.inc("accountBalance", money);
        UpdateResult result = mongoTemplate.updateFirst(q, update, isNotChannel ? DistributorAccount.class : DistributorChannel.class);
        log.info("accountId:{} money:{},roll back account result:{}", accountId, money, result);
    }

    public DistributorAccount queryAccountByUserId(String userId) {
        DistributorAccount account = mongoTemplate.findOne(new Query(Criteria.where("accountId").is(userId)), DistributorAccount.class);
        return account;
    }

}
